/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.admin.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.google.common.collect.Lists;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.springblade.common.utils.CommonUtil;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tenant.annotation.TenantIgnore;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.admin.dto.*;
import org.springblade.modules.admin.dto.plan.*;
import org.springblade.modules.admin.dto.rescue.TlwMalfunctionRescueDTO;
import org.springblade.modules.admin.entity.*;
import org.springblade.modules.admin.enums.*;
import org.springblade.modules.admin.service.*;
import org.springblade.modules.admin.vo.*;
import org.springblade.modules.admin.vo.image.by.ByPlanImageListVO;
import org.springblade.modules.admin.vo.plan.*;
import org.springblade.modules.admin.vo.task.AddPlanVO;
import org.springblade.modules.admin.vo.task.MaintainPlanTableVO;
import org.springblade.modules.admin.vo.task.ModifyPlanVO;
import org.springblade.modules.system.entity.User;
import org.springblade.modules.system.service.IUserService;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 维保任务表 控制器
 *
 * @author ysq
 * @since 2022-03-09
 */
@RestController
@AllArgsConstructor
@RequestMapping("admin/maintainPlanContent")
@Api(value = "维保任务表", tags = "维保任务表接口")
public class MaintainPlanContentController extends BladeController {

	private final IMaintainPlanContentService maintainPlanContentService;
	private final MaintainPlanUserDayCountService planUserDayCountService;
	private final MaintainMalfunctionLogService malfunctionLogService;
	private final IMaintainPlanContentExtService maintainPlanContentExtService;
	private final ElevatorService elevatorService;
	private final PlanHistoryImageService planHistoryImageService;
	private final IMaintainMalfunctionWorkService maintainMalfunctionWorkService;
	private final IMaintainSignService signService;
	private final IUserService userService;
	private final IMaintainPlanContentUserService maintainPlanContentUserService;

	@PostMapping("/speedRemovePlan")
	@ApiOperationSupport(order = 1)
	@ApiOperation(value = "清除全部计划", notes = "传入ids")
	public R speedRemovePlan(@ApiParam(value = "电梯集合", required = true) @RequestParam String elevatorIds,
							 @ApiParam(value = "开始时间") @RequestParam(required = false) Date startTime,
							 @ApiParam(value = "结束时间") @RequestParam(required = false) Date endTime) {
		maintainPlanContentService.deleteDateIntervalTask(Func.toLongList(elevatorIds), startTime, endTime);
		return R.status(true);
	}

	@PostMapping("/deleteTaskByPlanId")
	@ApiOperationSupport(order = 2)
	@ApiOperation(value = "根据planId集合删除计划", notes = "传入ids")
	public R deleteTaskByPlanId(@ApiParam(value = "planId集合", required = true) @RequestParam List<Long> planIds) {
		maintainPlanContentService.deleteTaskByPlanId(planIds, null);
		return R.status(true);
	}

	@GetMapping("/getMaintainPlans")
	@ApiOperationSupport(order = 3)
	@ApiOperation(value = "根据给定年份查保养计划")
	public R<TreeMap<String, List<MaintainPlanContentVO>>> getMaintainPlans(@RequestParam List<Long> elevatorIds,
																			Integer year,
																			@RequestParam(required = false) List<Long> templateIds) {
		return R.data(maintainPlanContentService.getMaintainPlans(elevatorIds, year, templateIds));
	}

	@PostMapping("/addPlan")
	@ApiOperationSupport(order = 4)
	@ApiOperation(value = "增加排期")
	public R addPlan(@Valid @RequestBody AddPlanVO addplanVO) {
		return R.status(maintainPlanContentService.addPlan(addplanVO));
	}

	@PostMapping("/modifyPlan")
	@ApiOperationSupport(order = 5)
	@ApiOperation(value = "更改排期计划日期")
	public R modifyPlan(@Valid @RequestBody ModifyPlanVO modifyPlanVO) {
		return R.status(maintainPlanContentService.modifyPlan(modifyPlanVO));
	}

	@PostMapping("/modifyTemplate")
	@ApiOperationSupport(order = 5)
	@ApiOperation(value = "更改排期保养类型")
	public R modifyTemplate(@Valid @RequestBody ModifyPlanVO modifyPlanVO) {
		return R.status(maintainPlanContentService.modifyTemplate(modifyPlanVO));
	}

	@PostMapping("/modifyPlanUser")
	@ApiOperationSupport(order = 5)
	@ApiOperation(value = "更改保养排期执行人")
	public R modifyPlanUser(@Valid @RequestBody ModifyPlanUserDTO modifyPlanUserDTO) {
		return R.status(maintainPlanContentService.modifyPlanUser(modifyPlanUserDTO));
	}

	@PostMapping("/batchModifyPlanUser")
	@ApiOperationSupport(order = 5)
	@ApiOperation(value = "批量设置计划执行人")
	public R<Integer> batchModifyPlanUser(@Valid @RequestBody BatchModifyPlanUserDTO planUserDTO) {
		return R.data(maintainPlanContentService.batchModifyPlanUser(planUserDTO));
	}

	@PostMapping("/batchModifyPlanUserByPlanIds")
	@ApiOperationSupport(order = 5)
	@ApiOperation(value = "批量设置计划执行人(根据planId)")
	public R<Integer> batchModifyPlanUserByPlanIds(@Valid @RequestBody BatchModifyPlanUserByPlanIdsDTO planUserDTO) {
		return R.data(maintainPlanContentService.batchModifyPlanUserByPlanIds(planUserDTO));
	}

	@ApiOperationSupport(order = 5)
	@ApiOperation("下载批量删除计划执行人错误数据")
	@PostMapping("down/batch/del/user/err/excel")
	public void downBatchDelUserErrExcel(HttpServletResponse response) throws IOException {
		maintainPlanContentService.downBatchDelUserErrExcel(response);
	}

	@PostMapping("/BatchModifyPlanTemplate")
	@ApiOperationSupport(order = 5)
	@ApiOperation(value = "批量更换保养类型")
	public R BatchModifyPlanTemplate(@Valid @RequestBody BatchModifyPlanTemplateDTO planTemplateDTO) {
		return R.status(maintainPlanContentService.BatchModifyPlanTemplate(planTemplateDTO));
	}

	@GetMapping("/checkPlanDeferRecord")
	@ApiOperationSupport(order = 5)
	@ApiOperation(value = "检验当前任务是否存在日期调整记录")
	public R checkPlanDeferRecord(@RequestParam Long planId) {
		return R.data(maintainPlanContentService.checkPlanDeferRecord(planId));
	}

	@GetMapping("/getDeptMaintainPlan")
	@ApiOperationSupport(order = 6)
	@ApiOperation(value = "根据部门查询整体保养排期")
	public R<List<MaintainPlanTableVO>> getDeptMaintainPlan(@ApiParam(value = "部门id") Long deptId, @ApiParam(value = "日期（yyyy-MM）") String yearMonth) {
		return R.data(maintainPlanContentService.getDeptMaintainPlan(deptId, yearMonth));
	}

	@PostMapping("/planList/new")
	@ApiOperationSupport(order = 7)
	@ApiOperation(value = "保养列表")
	public R<IPage<PlanListVo>> planListNew(@RequestBody PlanSearchDTO planSearchDTO) {
		if (planSearchDTO.getScoreType() != null && Convert.toInt(planSearchDTO.getScoreStatus(), -1) == 0) {
			return R.data(new Page<>());
		} else {
			if (planSearchDTO.getScoreType() != null) {
				planSearchDTO.setScoreStatus(1);
			}
		}
		return R.data(maintainPlanContentService.planListNew(planSearchDTO));
	}

	@TenantIgnore
	@PostMapping("/only/day/list")
	@ApiOperationSupport(order = 7)
	@ApiOperation(value = "日历视图单日排期列表")
	public R<IPage<PlanListVo>> selectPlanOnlyDayList(@RequestBody PlanSearchDTO planSearchDTO) {
		return R.data(maintainPlanContentService.selectPlanOnlyDayList(planSearchDTO));
	}

	@GetMapping("/by/plan/data/screen")
	@ApiOperationSupport(order = 7)
	@ApiOperation(value = "保养任务列表-数据看板")
	public R<ByPlanDataScreenVO> byPlanDataScreen() {
		return R.data(maintainPlanContentService.byPlanDataScreen());
	}

	@PostMapping("/maintain/calendar/screen")
	@ApiOperationSupport(order = 8)
	@ApiOperation(value = "保养排期日历视图")
	public R<List<MaintainCalendarScreenVO>> maintainCalendarScreen(@Valid @RequestBody MaintainCalendarScreenDTO calendarScreenDTO) {
		return R.data(maintainPlanContentService.maintainCalendarScreen(calendarScreenDTO));
	}

	@PostMapping("/malfunctionList")
	@ApiOperation(value = "维修列表")
	public R<IPage<PlanListVo>> malfunctionList(@RequestBody PlanSearchDTO planSearchDTO) {
		return R.data(maintainPlanContentService.malfunctionList(planSearchDTO));
	}

	/**
	 * 获取保养工单及上次保养工单时间
	 *
	 * @param planId
	 * @return
	 */
	@GetMapping("/simple/detail")
	public R<Map<String, Object>> planSimpleDetail(Long planId) {
		return R.data(maintainPlanContentService.selectPlanSimpleDetail(planId));
	}

	@PostMapping("/planList/down/excel")
	@ApiOperation(value = "下载保养工单")
	public void planListDownExcel(HttpServletResponse response, @RequestBody PlanSearchDTO planSearchDTO) throws IOException {
		planSearchDTO.setSize(0);
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		List<PlanListVo> records;
		if (planSearchDTO.getScoreType() != null && Convert.toInt(planSearchDTO.getScoreStatus(), -1) == 0) {
			records = new ArrayList<>(16);
		} else {
			if (planSearchDTO.getScoreType() != null) {
				planSearchDTO.setScoreStatus(1);
			}
			records = maintainPlanContentService.planListNewDown(planSearchDTO).getRecords();
		}
		try {
			List<List<String>> rows = new ArrayList<>();
			List<String> title = CollUtil.newArrayList
				("工单ID", "执行人", "实际执行人", "组别", "项目", "梯号", "电梯状态", "任务描述", "计划时间", "开始时间", "完成时间", "逾期日期",
					"当前状态", "时效", "时长标识", "协同工时累计总和(分钟)", "工单实际作业时长(分钟)", "评分状态", "工单评分", "有效判定", "计划调整状态", "调整证明", "已提交调整数量", "任务年审类型", "逾期", "电梯类型"
					, "签到签退异常状态", "维保签字时间", "提交时间", "电梯唯一识别码", "单位内编号");
			rows.add(title);
			for (PlanListVo planListVo : records) {
				List<String> row = new ArrayList<>(17);
				row.add(Convert.toStr(planListVo.getPlanId(), ""));
				if (planListVo.getPlanStatus() == TaskStatusEnum.BY_DISABLE.getStatus() || planListVo.getPlanStatus() == TaskStatusEnum.BY_PROCESSED.getStatus()) {
					row.add(Convert.toStr(planListVo.getRealNames(), ""));
					row.add("/");
				} else {
					row.add(Convert.toStr(planListVo.getPersonnelRealNames(), ""));
					row.add(Convert.toStr(planListVo.getRealNames(), ""));
				}
				row.add(Convert.toStr(planListVo.getGroupName(), ""));
				row.add(Convert.toStr(planListVo.getBuildingName(), ""));
				row.add(Convert.toStr(planListVo.getElevatorNo(), ""));
				row.add(Convert.toInt(planListVo.getIsStop(), 1) == 1 ? "正常" : "停梯");
				row.add(Convert.toStr(planListVo.getPlanCheckName(), ""));
				row.add(Convert.toStr(planListVo.getPlanDate(), ""));
				row.add(Convert.toStr(planListVo.getPlanStartTime(), ""));
				row.add(Convert.toStr(planListVo.getPlanEndTime(), ""));
				row.add(Func.isNotEmpty(planListVo.getOverdueDate()) ? DateUtil.formatDate(planListVo.getOverdueDate()) : "");
				row.add(Convert.toStr(TaskStatusEnum.getTaskName(MaintainPlanContentEnum.MAINTAIN_BY.getId(), planListVo.getPlanStatus()), ""));
				row.add(Convert.toStr(planListVo.getOverdueDayName(), ""));
				row.add(Convert.toStr(planListVo.getWorkMinuteName(), ""));
				row.add(Convert.toStr(planListVo.getWorkMinute(), "0"));
				row.add(Convert.toStr(planListVo.getWorkDuration(), "0"));
				String scoreStatusName;
				if (planListVo.getPlanStatus() == TaskStatusEnum.BY_SIGN.getStatus() && planListVo.getScoreStatus() != 1) {
					scoreStatusName = "待评分";
				} else if (planListVo.getScoreStatus() == 1) {
					scoreStatusName = "已评分";
				} else {
					scoreStatusName = "/";
				}
				row.add(Convert.toStr(scoreStatusName, ""));
				if (planListVo.getPlanStatus() == TaskStatusEnum.BY_SIGN.getStatus() && planListVo.getScoreStatus() != 1) {
					row.add("/");
					row.add("/");
				} else if (planListVo.getScoreStatus() == 1) {
					row.add(Convert.toStr(planListVo.getScore(), ""));
					if (planListVo.getScoreType() == 1) {
						row.add("有效订单");
					} else if (planListVo.getScoreType() == 2) {
						row.add("无效工单");
					} else if (planListVo.getScoreType() == 3) {
						row.add("无法判断");
					}
				} else {
					row.add("/");
					row.add("/");
				}
				row.add(Convert.toStr(planListVo.getDeferTypeName(), ""));
				row.add(Convert.toStr(planListVo.getDeferWarrantName(), ""));
				row.add(Convert.toStr(planListVo.getDeferCounts(), "0"));
				row.add(Convert.toStr(planListVo.getAuditType() == 1 ? "非年审" : "年审", "/"));
				row.add(Convert.toStr(planListVo.getOverdueTypeName(), "-"));
//				if (Convert.toInt(planListVo.getOverdueType(), 0) == -1) {
//					row.add("-");
//				} else if (Convert.toInt(planListVo.getOverdueType(), 0) == 1) {
//					row.add("已逾期");
//				} else if (Convert.toInt(planListVo.getOverdueType(), 0) == 2) {
//					row.add("未逾期" + (planListVo.getIsOverdueTomorrow() == true ? "(明天即将违规)" : ""));
//				} else {
//					row.add("-");
//				}

				row.add(Convert.toStr(Convert.toInt(planListVo.getUseType(), -1) == 1 ? "非年审" : "年审", "/"));
				// 新增签到签退异常状态
				List<PlanSignStatusVo> planSignStatusList = planListVo.getPlanSignStatusList();
				if (Func.isNotEmpty(planSignStatusList)) {
					StringBuffer sb = new StringBuffer();
					for (PlanSignStatusVo p : planSignStatusList) {
						String signStatusName = p.getSignStatusName();
						String signOutStatusName = p.getSignOutStatusName();
						if ("离线签到".equals(signStatusName) || "签到异常".equals(signStatusName) || "签退异常".equals(signOutStatusName)) {
							sb.append(p.getUserName() + ": ");
							if ("离线签到".equals(signStatusName) || "签到异常".equals(signStatusName)) {
								sb.append(signStatusName + " ");
							}

							if ("签退异常".equals(signOutStatusName)) {
								sb.append(signOutStatusName + " ");
							}
						} else {
							sb.append(" ");
						}
					}
					row.add(Convert.toStr(sb.toString()));
				} else {
					row.add("");
				}

				if (planListVo.getMaintainSignatureTime() == null) {
					row.add("/");
				} else {
					row.add(DateUtil.format(planListVo.getMaintainSignatureTime(), "yyyy-MM-dd HH:mm:ss"));
				}
				if (planListVo.getCommitTime() == null) {
					row.add("/");
				} else {
					row.add(DateUtil.format(planListVo.getCommitTime(), "yyyy-MM-dd HH:mm:ss"));
				}

				row.add(Convert.toStr(planListVo.getElevatorNum(), ""));
				row.add(Convert.toStr(planListVo.getRegisterNumber(), ""));
				rows.add(row);
			}
			String fileName = DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}


	/**
	 * 功能描述: 发送响应流方法
	 */
	private void setResponseHeader(HttpServletResponse response, String fileName) {
		try {
			try {
				fileName = new String(fileName.getBytes(), "ISO8859-1");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			response.setContentType("application/octet-stream;charset=ISO8859-1");
			response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
			response.addHeader("Pargam", "no-cache");
			response.addHeader("Cache-Control", "no-cache");
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * 2024-04-23 迭代：新增工单来源：
	 *
	 * @param planSearchDTO
	 * @return
	 */
	@PostMapping("v2/page")
	@ApiOperation(value = "维修记录分頁V2")
	public R<IPage<PlanResultVo>> page(@RequestBody PlanSearchDTOV2 planSearchDTO) {

		return R.data(maintainPlanContentService.getV2Page(planSearchDTO));
	}


	@PostMapping("/v2/page/down/excel")
	@ApiOperation(value = "下载保养工单")
	public void pageDownExcel(HttpServletResponse response, @RequestBody PlanSearchDTOV2 planSearchDTO) throws IOException {
		XSSFWorkbook workbook = null;
		//读取源文件
		ClassPathResource resource;
		try {
			resource = new ClassPathResource("templates/维修任务模板.xlsx");
			workbook = new XSSFWorkbook(resource.getInputStream());
		} catch (IOException e) {
			e.printStackTrace();
			throw new ServiceException("维修任务模板文件异常！");
		}
		//进行模板的克隆（接下来的操作都是针对克隆后的sheet）
		XSSFSheet sheet = workbook.cloneSheet(0);
		// 开始行
		final int startRow = 2;
		// 行数
		final int maxRow = 3000;
		// 清空内容
		for (int j = maxRow; j >= startRow; j--) {
			if (sheet.getRow(j) == null) {
				continue;
			}
			sheet.removeRow(sheet.getRow(j));
		}
		// 自定义样式1
		XSSFCellStyle style = workbook.createCellStyle();
		// 创建一个居中格式
		style.setAlignment(HorizontalAlignment.CENTER);
		// 垂直居中
		style.setVerticalAlignment(VerticalAlignment.CENTER);
		// 设置边框
		style.setBorderTop(BorderStyle.THIN);
		style.setBorderBottom(BorderStyle.THIN);
		style.setBorderLeft(BorderStyle.THIN);
		style.setBorderRight(BorderStyle.THIN);

		// 自定义样式2
		XSSFCellStyle style2 = workbook.createCellStyle();
		// 创建一个左对齐格式
		style2.setAlignment(HorizontalAlignment.LEFT);
		// 垂直居中
		style2.setVerticalAlignment(VerticalAlignment.CENTER);
		// 设置边框
		style2.setBorderTop(BorderStyle.THIN);
		style2.setBorderBottom(BorderStyle.THIN);
		style2.setBorderLeft(BorderStyle.THIN);
		style2.setBorderRight(BorderStyle.THIN);

		//移除workbook中的模板sheet
		workbook.removeSheetAt(0);
		//给sheet命名
		workbook.setSheetName(0, "维修任务");

		planSearchDTO.setSize(0);
		final List<PlanResultVo> records = maintainPlanContentService.getV2PageDownExcel(planSearchDTO);
		List<Long> planIds = records.stream().map(PlanResultVo::getId).collect(Collectors.toList());
		List<MalFunctionDetailVO> detailList = maintainMalfunctionWorkService.getMalfunctionWorkDetailList(planIds);
		Map<Long, MalFunctionDetailVO> detailMap = detailList.stream().collect(Collectors.toMap(MalFunctionDetailVO::getPlanId, Function.identity()));
		for (int i = 0; i < records.size(); i++) {
			// 数据封装
			PlanResultVo itemData = records.get(i);
			List<String> rowData = new ArrayList<>();
			rowData.add(itemData.getBuildingName());
			rowData.add(itemData.getElevatorAddress());
			rowData.add(itemData.getAuditType() == null ? "-" : itemData.getAuditType() == 1 ? "非年审" : "年审");
			rowData.add(Convert.toStr(itemData.getId()));
			rowData.add(itemData.getWorkOrderTypeName());
			if(Convert.toInt(itemData.getMalfunctionType(),-1)==1){
				rowData.add("部件损坏");
			}else if(Convert.toInt(itemData.getMalfunctionType(),-1)==2){
				rowData.add("安装问题");
			}else if(Convert.toInt(itemData.getMalfunctionType(),-1)==3){
				rowData.add("维保问题");
			}else{
				rowData.add("/");
			}

			//客户报障并且工单等于待开始，进行中已完成显示
			if (Convert.toInt(itemData.getWorkOrderType(), -1) == 2 && (itemData.getStatus() == TaskStatusEnum.WX_TO_START.getStatus() || itemData.getStatus() == TaskStatusEnum.WX_UNDER_WAY.getStatus() || itemData.getStatus() == TaskStatusEnum.WX_SIGN_FINISH.getStatus())) {
				rowData.add(itemData.getWxNumberDescs());
			} else {
				rowData.add("-");
			}
			rowData.add(itemData.getSourcePlanTypeName());
			rowData.add(itemData.getEmerProcTypeName());
			rowData.add(itemData.getUserNames());
			rowData.add(Convert.toStr(itemData.getRealUserNames(), "/"));
			rowData.add(Convert.toStr(itemData.getTaskUserTotal(), "/"));
			rowData.add(itemData.getApplyTsFlag() == null ? "否" : itemData.getApplyTsFlag() == 1 ? "是" : "否");
			rowData.add(itemData.getApplyPeFlag() == null ? "否" : itemData.getApplyPeFlag() == 1 ? "是" : "否");
			rowData.add(itemData.getAddUserFlag() == null ? "否" : itemData.getAddUserFlag() == 1 ? "是" : "否");
			rowData.add(itemData.getTransferUserFlag() == null ? "否" : itemData.getTransferUserFlag() == 1 ? "是" : "否");
			rowData.add(itemData.getGroupName());
			rowData.add(itemData.getIsStop() == 1 ? "正常" : "停梯");
			rowData.add(itemData.getFaultName());
			rowData.add(itemData.getPlanFromName());
			rowData.add(itemData.getElevatorIsStop() == 1 ? "正常运行" : "停梯");
			rowData.add(Convert.toStr(itemData.getPlanCheckTypeName(), ""));
			rowData.add(DateUtil.format(itemData.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
			rowData.add(DateUtil.format(itemData.getBeginTime(), "yyyy-MM-dd HH:mm:ss"));
			rowData.add(DateUtil.format(itemData.getEndTime(), "yyyy-MM-dd HH:mm:ss"));
			rowData.add(itemData.getWorkMinute() == null ? "/" : Convert.toStr(itemData.getWorkMinute()));
			rowData.add(Convert.toStr(itemData.getProblemItemName()));
			rowData.add(itemData.getImageNum().toString());
			rowData.add(Convert.toStr(itemData.getOwnPlanIds(), "0"));
			rowData.add(itemData.getStatus() == 0 ? "待指派" : "已指派");
			rowData.add(itemData.getApproveStatusName());
			if (Convert.toStr(itemData.getApproveStatusName(), "").equals("已撤回")) {
				rowData.add("/");
			} else {
				if (itemData.getStatusName().equals("已驳回") || itemData.getStatusName().equals("待处理")) {
					rowData.add("/");
				} else {
					rowData.add(itemData.getStatusName());
				}
			}

			MalFunctionDetailVO detailData = detailMap.get(itemData.getId());
			if (detailData != null && detailData.getMalFunctionHistories() != null) {
				List<MalFunctionHistoryVO> historyList = detailData.getMalFunctionHistories();
				Map<Long, List<MalFunctionHistoryVO>> historyMap = historyList.stream().collect(Collectors.groupingBy(MalFunctionHistoryVO::getWorksId));

				List<FaultMessageVO> faultMessageList = detailData.getFaultMessageList().stream().filter(t -> !Func.equalsSafe(t.getPartsIds(), "0")).collect(Collectors.toList());
				for (int k = 0; k < faultMessageList.size(); k++) {
					FaultMessageVO faultMessage = faultMessageList.get(k);
					rowData.add(Convert.toStr(faultMessage.getPartsName()));
					StringBuilder mfDesc = new StringBuilder("故障描述：").append(faultMessage.getMfDesc()).append("\n");
					mfDesc.append(faultMessage.getCreateUserName()).append(" ").append(DateUtil.format(faultMessage.getMsgCreateTime(), "yyyy-MM-dd HH:mm:ss"));
					rowData.add(mfDesc.toString());

					if (faultMessage.getFaultReasonNames() == null && faultMessage.getFaultDesc() == null && faultMessage.getPartsOpe() == null) {
						rowData.add("");
					} else {
						String faultDesc = faultMessage.getFaultReasonNames() == null ? "" : faultMessage.getFaultReasonNames().stream().collect(Collectors.joining(","));
						StringBuilder faultReason = new StringBuilder("故障原因：").append(faultDesc).append("\n");
						faultReason.append("故障说明：").append(Convert.toStr(faultMessage.getFaultDesc(), "")).append("\n");
						// 1-无需更换 2-建议更换 3-已更换
						faultReason.append("配件更换：").append(faultMessage.getPartsOpe() == null ? "" : faultMessage.getPartsOpe() == 1 ? "无需更换" : faultMessage.getPartsOpe() == 2 ? "建议更换" : "已更换").append("\n");
						faultReason.append(Convert.toStr(faultMessage.getFaultSubmitUserName(), "")).append(" ").append(Convert.toStr(DateUtil.format(faultMessage.getFaultSubmitTime(), "yyyy-MM-dd HH:mm:ss"), ""));
						rowData.add(faultReason.toString());
					}

					List<MalFunctionHistoryVO> historyItemList = historyMap.get(faultMessage.getId());
					if (historyItemList == null) {
						rowData.add("");
					} else {
						StringBuilder historyDesc = new StringBuilder();
						for (int h = 0; h < historyItemList.size(); h++) {
							MalFunctionHistoryVO historyItem = historyItemList.get(h);
							historyDesc.append(h + 1).append("、").append("维修类型：").append(historyItem.getDisposeType() == null ? "" : historyItem.getDisposeType() == 1 ? "设备检修" : "更换部件").append("\n");
							historyDesc.append("维修描述：").append(historyItem.getDisposeDesc()).append("\n");
							String recordDesc = historyItem.getMalfunctionRecordVO().stream().findFirst().map(t -> t.getUserName() + " " + DateUtil.format(t.getUpdateTime(), "yyyy-MM-dd HH:mm:ss")).get();
							historyDesc.append(recordDesc);
						}
						rowData.add(historyDesc.toString());
					}
				}
			}

			XSSFRow row = sheet.createRow(i + startRow);
			row.setHeightInPoints(30);
			for (int r = 0; r < 51; r++) {
				XSSFCell cell = row.createCell(r);
				if (r < rowData.size()) {
					cell.setCellValue(Convert.toStr(rowData.get(r)));
				}

				if (r < 31) {
					cell.setCellStyle(style);
				} else {
					cell.setCellStyle(style2);
				}
			}

		}

		String fileName = DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
		ServletOutputStream outputStream = null;
		try {
			outputStream = response.getOutputStream();
			CommonUtil.setResponseHeader(response, fileName);
			workbook.write(outputStream);
			outputStream.flush();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (workbook != null) {
					workbook.close();
				}
				outputStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}


	@GetMapping("/getMalfunctionHistoryList")
	@ApiOperationSupport(order = 7)
	@ApiOperation(value = "获取维修记录详情")
	public R<MalRecordDetailVO> getMalfunctionHistoryList(Long planId) {
		return R.data(maintainPlanContentService.getMalfunctionHistoryList(planId));
	}

	@TenantIgnore
	@GetMapping("/work/detail")
	@ApiOperationSupport(order = 8)
	@ApiOperation(value = "任务明细")
	public R<MaintainPlanContentWorkDetailVo> workDetail(@RequestParam("planId") Long planId) {
		MaintainPlanContentWorkDetailVo maintainPlanContentWorkDetailVo = maintainPlanContentService.workDetail(planId);
		return R.data(maintainPlanContentWorkDetailVo);
	}

	/**
	 * 维修报障新增
	 */
	@PostMapping("/save")
	@ApiOperationSupport(order = 9)
	@ApiOperation(value = "维修报障新增")
	public R save(@Valid @RequestBody PlanContentSaveDto planContentSaveDto) {
		return R.data(maintainPlanContentService.savePlan(planContentSaveDto));
	}

	/**
	 * 维修报障修改
	 *
	 * @param planContentUpdateDto 修改目标信息
	 * @return 是否成功
	 */
	@PostMapping("/update/message")
	@ApiOperationSupport(order = 41)
	@ApiOperation(value = "维修报障信息修改")
	public R<Boolean> updateMessage(@Valid @RequestBody PlanContentUpdateDto planContentUpdateDto) {
		boolean b = maintainPlanContentService.updateMessage(planContentUpdateDto);
		return R.data(b);
	}

	/**
	 * 梯联网-维修报障新增
	 */
	@PostMapping("/tlw/save")
	@ApiOperationSupport(order = 9)
	@ApiOperation(value = "梯联网-维修报障新增")
	public R tlwSave(@Valid @RequestBody TlwMalfunctionPlanSaveDTO tlwMalfunctionPlanSaveDTO) {
		return R.status(maintainPlanContentService.tlwMalPlanSave(tlwMalfunctionPlanSaveDTO));
	}

	@GetMapping("/tlw/malfunctioning/list")
	@ApiOperationSupport(order = 9)
	@ApiOperation(value = "梯联网-获取正在维修列表")
	public R<List<TlwMalfunctioningListVO>> tlwMalfunctioningList(@ApiParam(value = "设备id", required = true) @RequestParam String deviceId) {
		return R.data(maintainPlanContentService.tlwMalfunctioningList(deviceId));
	}

	@GetMapping("tlw/malfunction/detail")
	@ApiOperationSupport(order = 9)
	@ApiOperation(value = "梯联网-维修详情")
	public R<TlwMalfunctionDetailVO> tlwMalfunctionDetail(@ApiParam(value = "报警id", required = true) @RequestParam String tlwJoinId) {
		return R.data(maintainPlanContentService.tlwMalfunctionDetail(tlwJoinId));
	}

	/**
	 * 保养工单评分/修改评分
	 */
	@PostMapping("/by/score/submit")
	@ApiOperationSupport(order = 10)
	@ApiOperation(value = "保养工单评分")
	public R byScoreSubmit(@Valid @RequestBody MaintainPlanContentExt maintainPlanContentExt) {
		return R.status(maintainPlanContentService.byScoreSubmit(maintainPlanContentExt));
	}

	@TenantIgnore
	@PostMapping("/test/add/count")
	public R<?> test() {
//		signService.systemSignByNotSignOut(MaintainPlanContentEnum.MAINTAIN_BY.getId());
//		planUserDayCountService.updateCheckPlanUserDayCount();
		planUserDayCountService.updatePlanUserDayCount(MaintainPlanContentEnum.MAINTAIN_BY.getId());
		return R.status(true);
	}

	/**
	 * 脚本：增加历史维修任务每天每人统计
	 *
	 * @return
	 */
	@TenantIgnore
	@PostMapping("/test/wx/history/add/day/count")
	public R<?> wxHistoryAddDayCount() {
		planUserDayCountService.wxHistoryAddDayCount();
		return R.status(true);
	}

	@TenantIgnore
	@PostMapping("/wx/add/day/count/task")
	public R<?> wxAddDayCountTask() {
		planUserDayCountService.updateWxPlanUserDayCount(String.format("%s,%s", MaintainPlanContentEnum.MAINTAIN_WX.getId(), MaintainPlanContentEnum.MAINTAIN_PARTS.getId()));
		return R.status(true);
	}

	/**
	 * 2023-02-14
	 * 获取工单的额外其它信息
	 *
	 * @param planId 工单id
	 * @return 数据集
	 */
	@GetMapping("getPlanOtherInfo")
	@ApiOperation(value = "取工单的额外其它信息")
	public R<PlanOtherInfoVo> getPlanOtherInfo(@RequestParam("planId") Long planId) {
		PlanOtherInfoVo planOtherInfoVo = maintainPlanContentService.getPlanOtherInfo(planId);
		return R.data(planOtherInfoVo);
	}

	@GetMapping("future/maintain/planstatistical")
	@ApiOperation(value = "根据用户id查询未来保养排期计划统计")
	public R<FutureMaintainPlanStatisticalVO> futureMaintainPlanStatistical(@RequestParam("userId") Long userId) {
		return R.data(maintainPlanContentService.futureMaintainPlanStatistical(userId));
	}

	@PostMapping("move/user/future/plan")
	@ApiOperation(value = "离职人员-变更计划执行人")
	public R moveUserFuturePlan(@Valid @RequestBody MoveUserPlanDTO moveUserPlanDTO) {
		return R.status(maintainPlanContentService.moveUserFuturePlan(moveUserPlanDTO));
	}

	@PostMapping("del/user/future/plan")
	@ApiOperation(value = "离职人员-移除计划执行人")
	public R delUserFuturePlan(@ApiParam(value = "离职人员用户id") @RequestParam("leaveUserId") Long leaveUserId) {
		return R.status(maintainPlanContentService.delUserFuturePlan(leaveUserId));
	}

	@GetMapping(value = "part/Approve/statistical")
	@ApiOperation(value = "查询配件审批统计（组长审批节点）")
	public R<PlanApproveVO> partApproveStatistical(@RequestParam Long userId) {
		return R.data(maintainPlanContentService.partApproveStatistical(userId));
	}

	/**
	 * 新增救援工单
	 */
	@PostMapping("/add/rescue/plan")
	@ApiOperationSupport(order = 8)
	@ApiOperation(value = "新增救援工单")
	public R<Long> addRescuePlan(@Valid @RequestBody MaintainMalfunctionRescue maintainMalfunctionRescue) {
		return R.data(maintainPlanContentService.addRescuePlan(maintainMalfunctionRescue));
	}

	/**
	 * 新增救援工单
	 */
	@PostMapping("tlw/add/rescue/plan")
	@ApiOperationSupport(order = 8)
	@ApiOperation(value = "梯联网-新增救援工单")
	public R addTlwRescuePlan(@Valid @RequestBody TlwMalfunctionRescueDTO tlwMalfunctionRescue) {
		return R.status(maintainPlanContentService.addTlwRescuePlan(tlwMalfunctionRescue) > 0);
	}

	/**
	 * 救援工单详情列表
	 */
	@GetMapping("/rescue/detail/list")
	@ApiOperationSupport(order = 9)
	@ApiOperation(value = "救援工单详情列表")
	public R<List<RescueDetailVO>> rescueDetailListByElevatorId(@ApiParam(value = "项目id") @RequestParam("buildingId") Long buildingId,
																@ApiParam(value = "电梯id") @RequestParam("elevatorId") Long elevatorId) {
		return R.data(maintainPlanContentService.rescueDetailListByElevatorId(buildingId, elevatorId));
	}

	/**
	 * @return
	 * @Author lsay
	 * @Description {
	 * 获取人员离职工单统计
	 * }
	 * @Date 14:35 2023/3/15
	 * @Param
	 **/
	@GetMapping("/dimission/count")
	public R<UserDimissionCountVO> selectDimissionCount(Long userId) {
		return R.data(maintainPlanContentService.selectUserDimissionCount(userId));
	}

	/**
	 * @return
	 * @Author lsay
	 * @Description {
	 * 人员离职清除待开始及进行中（没有作业记录）的工单关联关系
	 * }
	 * @Date 9:33 2023/3/20
	 * @Param
	 **/
	@DeleteMapping("/user/clear")
	public R<Boolean> deleteDimissionUserPlan(Long userId) {
		return R.data(maintainPlanContentService.deleteDimissionUserPlan(userId));
	}

	@TenantIgnore
	@GetMapping("/dispose/signature/time")
	public R<Boolean> disposeSignatureTime() {
		return R.data(maintainPlanContentService.disposeSignatureTime());
	}


	@PostMapping("updatePlanContent")
	@ApiOperation("更新工单信息")
	public R<Boolean> updatePlanContent(@RequestBody MaintainPlanContent maintainPlanContent) {
		if (maintainPlanContent.getTypeStatus() == MaintainPlanContentEnum.MAINTAIN_WX.getId()) {

			MaintainPlanContent planContent = maintainPlanContentService.getById(maintainPlanContent.getId());
			if (planContent.getStatus() == TaskStatusEnum.WX_SIGN_FINISH.getStatus()) {
				throw new ServiceException("工单状态已变更请刷新页面");
			}

			//等于维修变成已关闭时,新增log日志
			if (maintainPlanContent.getStatus().equals(TaskStatusEnum.WX_CHECK_PASS_CLOSED.getStatus())) {
				malfunctionLogService.insertWorkLog(maintainPlanContent.getId(), WorkTrackStatusEnum.CLOSE.getTypeName(), TaskStatusEnum.WX_CHECK_PASS_CLOSED.getStatus());
			}

			//更新关闭信息
			List<MaintainMalfunctionWork> maintainMalfunctionWorks = maintainMalfunctionWorkService.list(Wrappers.<MaintainMalfunctionWork>lambdaQuery().eq(MaintainMalfunctionWork::getPlanId, planContent.getId()));
			maintainMalfunctionWorks.forEach(o -> {
				o.setCloseDesc(maintainPlanContent.getCloseDesc());
				o.setCloseUserName(AuthUtil.getNickName());
			});
			maintainMalfunctionWorkService.updateBatchById(maintainMalfunctionWorks);
			//如果等于维修改为系统代签
			if (planContent.getStatus().equals(TaskStatusEnum.WX_UNDER_WAY.getStatus())) {
				List<MaintainSign> signs = signService.list(Wrappers.<MaintainSign>lambdaQuery().eq(MaintainSign::getPlanId, planContent.getId()).isNull(MaintainSign::getSignOutTime));
				if (signs.size() > 0) {
					Integer status = 2;//系统代签
					signs.forEach(o -> {
						o.setStatus(status);
						o.setSignOutStatus(SignStatusEnum.NORMAL.getId());
						o.setSignOutTime(new Date());
					});
					signService.updateBatchById(signs);
					//第二步插入log日志
					List<Long> signUserIds = signs.stream().map(MaintainSign::getUserId).collect(Collectors.toList());
					List<String> userNames = userService.listByIds(signUserIds).stream().map(User::getRealName).collect(Collectors.toList());
					PlanLogDTO planLogDTO = new PlanLogDTO();
					planLogDTO.setExpDesc("系统代签");
					malfunctionLogService.insertWorkLog(maintainPlanContent.getId(), "签退", maintainPlanContent.getStatus(), JSON.toJSONString(planLogDTO), AuthUtil.getUserId(), String.join("/", userNames));
					//将已签到的用户头像头像设置为不亮
					List<MaintainPlanContentUser> closeUserHears = maintainPlanContentUserService.list(Wrappers.<MaintainPlanContentUser>lambdaQuery().eq(MaintainPlanContentUser::getPlanId, planContent.getId()).in(MaintainPlanContentUser::getUserId, signs.stream().map(MaintainSign::getUserId).distinct().collect(Collectors.toList())));

					for (MaintainPlanContentUser maintainPlanContentUser : closeUserHears) {
						PlanUserSignDetailVo signUser = maintainPlanContentUser.getSignUser();
						signUser.setIsPlanStatus(false);
						maintainPlanContentUser.setSignInfo(JSON.toJSONString(signUser));
					}
					maintainPlanContentUserService.updateBatchById(closeUserHears);
				}
			}
		}
		return R.data(maintainPlanContentService.updateById(maintainPlanContent));
	}


	@GetMapping("init")
	@ApiOperation("工单数据初始化")
	public R<String> init() {
		List<Elevator> list = elevatorService.list();
		List<PlanInitVo> planList = maintainPlanContentService.getPlanListInitData();
		Map<Long, List<PlanInitVo>> listMap = planList.stream().collect(Collectors.groupingBy(PlanInitVo::getElevatorId));
		List<MaintainPlanContentExt> updateList = new ArrayList<>(10000);
		for (Elevator elevator : list) {
			List<PlanInitVo> planInitVos = listMap.get(elevator.getId());
			if (planInitVos == null || planInitVos.size() == 0) {
				continue;
			}
			planInitVos = planInitVos.stream().sorted(new Comparator<PlanInitVo>() {
				@Override
				public int compare(PlanInitVo o1, PlanInitVo o2) {
					Date d1 = o1.getMaintainSignatureTime();
					Date d2 = o2.getMaintainSignatureTime();
					return d1.compareTo(d2);
				}
			}).collect(Collectors.toList());

			for (PlanInitVo planInitVo : planInitVos) {
				int index = planInitVos.indexOf(planInitVo);
				if (index == 0) {
					DateTime dateTime = DateUtil.offsetDay(planInitVo.getPlanDate(), 15);
					planInitVo.setOverdueDate(dateTime);
				} else {
					PlanInitVo planInitVo1 = planInitVos.get(index - 1);
					DateTime dateTime = DateUtil.offsetDay(planInitVo1.getMaintainSignatureTime(), 15);
					planInitVo.setOverdueDate(dateTime);
				}
				MaintainPlanContentExt maintainPlanContentExt = new MaintainPlanContentExt();
				maintainPlanContentExt.setId(planInitVo.getPlanExtId());
				maintainPlanContentExt.setOverdueDate(planInitVo.getOverdueDate());
				updateList.add(maintainPlanContentExt);
			}

		}

		List<List<MaintainPlanContentExt>> partition = Lists.partition(updateList, 20);
		for (List<MaintainPlanContentExt> list2 : partition) {
			maintainPlanContentExtService.updateBatchById(list2);
		}
		return R.data("成功");
	}

	/**
	 * @return 错误工单个数
	 * @Author lsay
	 * @Description {
	 * 工单多模板配置
	 * }
	 * @Date 15:00 2024/2/26
	 * @Param
	 **/
	@TenantIgnore
	@PostMapping("/config/template")
	public R<Integer> configTemplate(@RequestBody PlanTemplateSubmitDTO submitDTO) {
		return R.data(maintainPlanContentService.configTemplate(submitDTO));
	}

	@ApiOperation("下载批量删除工单模板错误数据")
	@TenantIgnore
	@PostMapping("down/batch/del/template/err/excel")
	public void downBatchDelTemplateErrExcel(HttpServletResponse response) throws IOException {
		maintainPlanContentService.downBatchDelTemplateErrExcel(response);
	}

	/**
	 * 查询保养工单历史图片
	 *
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @return 原VO
	 */
	@TenantIgnore
	@GetMapping("/select/by/all/image")
	public R<List<ByPlanImageListVO>> selectByAllImage(String startDate, String endDate) {
		return R.data(maintainPlanContentService.selectByAllImage(startDate, endDate));
	}

	/**
	 * 新增工单历史图片处理结果
	 *
	 * @param entityList 图片列表
	 * @return 是否成功
	 */
	@TenantIgnore
	@PostMapping("/save/all/image")
	public R<Boolean> saveAllImage(@RequestBody List<PlanHistoryImage> entityList) {
		return R.data(planHistoryImageService.saveAllImage(entityList));
	}

	/**
	 * 2024-02-24
	 * 任务来源接口
	 */
	@GetMapping("/plan/source/list")
	public R<List<PlanSourceVO>> getPlanSourceList(Long planId) {
		List<PlanSourceVO> result = maintainPlanContentService.getPlanSourceList(planId);
		return R.data(result);
	}

	/**
	 * 2024-05-07
	 * 任务来源初始化接口
	 */
	@ApiOperation("初始化任务来源")
	@GetMapping("/plan/source/list/init")
	public R<Boolean> initPlanSourceList() {
		return R.data(maintainPlanContentService.initPlanSourceList());
	}

}
